package com.leslie.common.utils.poi;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.leslie.common.utils.poi.excelStrategy.ExcelStyleStrategy;
import com.leslie.common.utils.poi.excelStrategy.ExcelWidthStyleStrategy;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * EasyExcel工具类
 *
 * @author Leslie Lee
 * @TIME 1956/09/12
 * @version 2003/04/01
 */
public class EasyExcelUtils {

    /**
     * 导出数据到Excel，自适应列宽
     * 浏览器直接下载文件
     *
     * @param response  response
     * @param fileName  导出文件名称
     * @param sheetName 文件sheet名称
     * @param clazz     导出数据对应实体
     * @param list      导出数据集合
     * @throws Exception 异常信息
     */
    public static void downloadExcel(HttpServletResponse response, String fileName, String sheetName, Class<?> clazz, List<?> list) throws Exception {
        EasyExcel.write(getServletOutputStream(response, fileName), clazz)
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .sheet(sheetName)
                .doWrite(list);
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 多sheet导出
     * sheetNames、lists 数量必须一致
     * 浏览器直接下载文件
     *
     * @param response   response
     * @param fileName   导出文件名称
     * @param sheetNames 文件sheet名称，例：new String[]{"测试sheet1","测试sheet2"}
     * @param clazz      导出数据对应实体
     * @param lists      导出数据集合
     * @throws Exception 异常信息
     */
    public static void downloadExcel(HttpServletResponse response, String fileName, String[] sheetNames, Class<?> clazz, List<?>... lists) throws Exception {
        if (sheetNames.length != lists.length)
            throw new IllegalArgumentException("sheetNames、lists 数量不一致");
        try (ExcelWriter excelWriter = EasyExcel.write(getServletOutputStream(response, fileName), clazz)
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .build()) {
            for (int i = 0; i < sheetNames.length; i++) {
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetNames[i])
                        .build();
                excelWriter.write(lists[i], writeSheet);
            }
        }
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 多sheet，多表头导出
     * sheetNames、lists 数量必须一致
     * 浏览器直接下载文件
     *
     * @param response   response
     * @param fileName   导出文件名称
     * @param sheetNames 文件sheet名称，例：new String[]{"测试sheet1","测试sheet2"}
     * @param clazz      导出数据对应实体，例：new Class[]{"测试class1","测试class2"}
     * @param lists      导出数据集合
     * @throws Exception 异常信息
     */
    public static void downloadExcel(HttpServletResponse response, String fileName, String[] sheetNames, Class<?>[] clazz, List<?>... lists) throws Exception {
        if (sheetNames.length != lists.length || sheetNames.length != clazz.length)
            throw new IllegalArgumentException("sheetNames、clazz、lists 数量不一致");
        try (ExcelWriter excelWriter = EasyExcel.write(getServletOutputStream(response, fileName))
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .build()) {
            for (int i = 0; i < sheetNames.length; i++) {
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetNames[i])
                        .head(clazz[i])
                        .build();
                excelWriter.write(lists[i], writeSheet);
            }
        }
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 默认表头、内容样式
     * 浏览器直接下载文件
     *
     * @param response  response
     * @param fileName  导出文件名称
     * @param sheetName 文件sheet名称
     * @param clazz     导出数据对应实体
     * @param list      导出数据集合
     * @throws Exception 异常信息
     */
    public static void downloadExcelDefaultStyle(HttpServletResponse response, String fileName, String sheetName, Class<?> clazz, List<?> list) throws Exception {
        EasyExcel.write(getServletOutputStream(response, fileName), clazz)
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .registerWriteHandler(ExcelStyleStrategy.getHorizontalCellStyleStrategyDefault())
                .sheet(sheetName)
                .doWrite(list);
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 默认表头、内容样式
     * 多sheet导出
     * sheetNames、lists 数量必须一致
     * 浏览器直接下载文件
     *
     * @param response   response
     * @param fileName   导出文件名称
     * @param sheetNames 文件sheet名称
     * @param clazz      导出数据对应实体
     * @param lists      导出数据集合
     * @throws Exception 异常信息
     */
    public static void downloadExcelDefaultStyle(HttpServletResponse response, String fileName, String[] sheetNames, Class<?> clazz, List<?>... lists) throws Exception {
        if (sheetNames.length != lists.length)
            throw new IllegalArgumentException("sheetNames、lists 数量不一致");
        try (ExcelWriter excelWriter = EasyExcel.write(getServletOutputStream(response, fileName), clazz)
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .registerWriteHandler(ExcelStyleStrategy.getHorizontalCellStyleStrategyDefault())
                .build()) {
            for (int i = 0; i < sheetNames.length; i++) {
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetNames[i])
                        .build();
                excelWriter.write(lists[i], writeSheet);
            }
        }
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 自定义表头、内容样式
     * 浏览器直接下载文件
     *
     * @param response                   response
     * @param fileName                   导出文件名称
     * @param sheetName                  文件sheet名称
     * @param clazz                      导出数据对应实体
     * @param list                       导出数据集合
     * @param headIndexedColors          表头背景颜色
     * @param contentIndexedColors       内容背景颜色
     * @param fontName                   统一字体名称
     * @param headFontSize               表头字体大小
     * @param contentFontSize            内容字体大小
     * @param headBold                   表头是否字体加粗
     * @param contentBold                内容是否字体加粗
     * @param borderStyle                边框样式，例：粗细，点状等
     * @param wrapped                    是否自动换行
     * @param headHorizontalAlignment    表头水平对齐方式
     * @param headVerticalAlignment      表头垂直对齐方式
     * @param contentHorizontalAlignment 内容水平对齐方式
     * @param contentVerticalAlignment   内容垂直对齐方式
     * @param shrinkToFit                是否自适应收缩
     * @throws Exception 异常信息
     */
    public static void downloadExcelCustomizeStyle(HttpServletResponse response, String fileName, String sheetName, Class<?> clazz, List<?> list, short headIndexedColors,
                                                   short contentIndexedColors, String fontName, short headFontSize, short contentFontSize, boolean headBold, boolean contentBold,
                                                   BorderStyle borderStyle, boolean wrapped, HorizontalAlignment headHorizontalAlignment, VerticalAlignment headVerticalAlignment,
                                                   HorizontalAlignment contentHorizontalAlignment, VerticalAlignment contentVerticalAlignment, boolean shrinkToFit) throws Exception {
        EasyExcel.write(getServletOutputStream(response, fileName), clazz)
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .registerWriteHandler(ExcelStyleStrategy.getHorizontalCellStyleStrategyCustomize(headIndexedColors, contentIndexedColors, fontName, headFontSize, contentFontSize,
                        headBold, contentBold, borderStyle, wrapped, headHorizontalAlignment, headVerticalAlignment, contentHorizontalAlignment, contentVerticalAlignment, shrinkToFit))
                .sheet(sheetName)
                .doWrite(list);
    }

    /**
     * 导出数据到Excel
     * 自适应列宽
     * 自定义表头、内容样式
     * 多sheet、多表头导出
     * sheetNames、clazz、lists 数量必须一致
     * 浏览器直接下载文件
     *
     * @param response                   response
     * @param fileName                   导出文件名称
     * @param sheetNames                 文件sheet名称
     * @param clazz                      导出数据对应实体
     * @param lists                      导出数据集合
     * @param headIndexedColors          表头背景颜色
     * @param contentIndexedColors       内容背景颜色
     * @param fontName                   统一字体名称
     * @param headFontSize               表头字体大小
     * @param contentFontSize            内容字体大小
     * @param headBold                   表头是否字体加粗
     * @param contentBold                内容是否字体加粗
     * @param borderStyle                边框样式，例：粗细，点状等
     * @param wrapped                    是否自动换行
     * @param headHorizontalAlignment    表头水平对齐方式
     * @param headVerticalAlignment      表头垂直对齐方式
     * @param contentHorizontalAlignment 内容水平对齐方式
     * @param contentVerticalAlignment   内容垂直对齐方式
     * @param shrinkToFit                是否自适应收缩
     * @throws Exception 异常信息
     */
    public static void downloadExcelCustomizeStyle(HttpServletResponse response, String fileName, String[] sheetNames, Class<?>[] clazz, short headIndexedColors,
                                                   short contentIndexedColors, String fontName, short headFontSize, short contentFontSize, boolean headBold, boolean contentBold,
                                                   BorderStyle borderStyle, boolean wrapped, HorizontalAlignment headHorizontalAlignment, VerticalAlignment headVerticalAlignment,
                                                   HorizontalAlignment contentHorizontalAlignment, VerticalAlignment contentVerticalAlignment, boolean shrinkToFit, List<?>... lists) throws Exception {
        if (sheetNames.length != lists.length || sheetNames.length != clazz.length)
            throw new IllegalArgumentException("sheetNames、clazz、lists 数量不一致");
        try (ExcelWriter excelWriter = EasyExcel.write(getServletOutputStream(response, fileName))
                .registerWriteHandler(new ExcelWidthStyleStrategy())
                .registerWriteHandler(ExcelStyleStrategy.getHorizontalCellStyleStrategyCustomize(headIndexedColors, contentIndexedColors, fontName, headFontSize, contentFontSize,
                        headBold, contentBold, borderStyle, wrapped, headHorizontalAlignment, headVerticalAlignment, contentHorizontalAlignment, contentVerticalAlignment, shrinkToFit))
                .build()) {
            for (int i = 0; i < sheetNames.length; i++) {
                WriteSheet writeSheet = EasyExcel.writerSheet(i, sheetNames[i])
                        .head(clazz[i])
                        .build();
                excelWriter.write(lists[i], writeSheet);
            }
        }
    }

    /**
     * 设置导出文件名及编码
     *
     * @param response response
     * @param fileName 文件名
     * @return ServletOutputStream
     * @throws IOException 异常信息
     */
    private static ServletOutputStream getServletOutputStream(HttpServletResponse response, String fileName) throws IOException {
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        String newFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + newFileName + ".xlsx");
        return response.getOutputStream();
    }

}
